package ar.edu.unrn.lia.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;

import org.springframework.mail.SimpleMailMessage;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ar.edu.unrn.lia.dao.AlquilerDAO;
import ar.edu.unrn.lia.model.Alquiler;
import ar.edu.unrn.lia.model.Alquiler.AlquilerEstado;
import ar.edu.unrn.lia.model.AlquilerCuota;
import ar.edu.unrn.lia.model.AlquilerCuota.CoutaEstado;
import ar.edu.unrn.lia.model.CuotaPreIngreso;
import ar.edu.unrn.lia.model.CuotaPreIngreso.Concepto;
import ar.edu.unrn.lia.model.Movimiento.TipoMovimiento;
import ar.edu.unrn.lia.model.Propiedad.Estado;
import ar.edu.unrn.lia.service.AlquilerCuotaService;
import ar.edu.unrn.lia.service.AlquilerService;
import ar.edu.unrn.lia.service.ContableService;
import ar.edu.unrn.lia.service.CuentaService;
import ar.edu.unrn.lia.service.MovimientoService;
import ar.edu.unrn.lia.service.PropiedadService;
import ar.edu.unrn.lia.service.exception.BusinessException;
import ar.edu.unrn.lia.util.FuncionesDate;
import ar.edu.unrn.lia.util.MailBusness;
import ar.edu.unrn.lia.util.MensajesAplicacion;
import ar.edu.unrn.lia.util.MensajesLenguage;

@Named("alquilerService")
public class AlquilerServiceImpl implements AlquilerService {

	
	@Inject
	MailBusness mailBusness;
	
	
	public MailBusness getMailBusness() {
		return mailBusness;
	}

	public void setMailBusness(MailBusness mailBusness) {
		this.mailBusness = mailBusness;
	}

	@Inject
	AlquilerDAO entityDAO;
	
	@Inject
	AlquilerCuotaService alquilerCuotaService;
	
	@Inject
	CuentaService cuentaService;
	
	@Inject
	MovimientoService movimientoService;
	
	@Inject
	ContableService contableService;
	
	@Inject
	PropiedadService propiedadService;
	
	

	public PropiedadService getPropiedadService() {
		return propiedadService;
	}

	public void setPropiedadService(PropiedadService propiedadService) {
		this.propiedadService = propiedadService;
	}

	public ContableService getContableService() {
		return contableService;
	}

	public void setContableService(ContableService contableService) {
		this.contableService = contableService;
	}

	public CuentaService getCuentaService() {
		return cuentaService;
	}

	public void setCuentaService(CuentaService cuentaService) {
		this.cuentaService = cuentaService;
	}

	public MovimientoService getMovimientoService() {
		return movimientoService;
	}

	public void setMovimientoService(MovimientoService movimientoService) {
		this.movimientoService = movimientoService;
	}

	public AlquilerCuotaService getAlquilerCuotaService() {
		return alquilerCuotaService;
	}

	public void setAlquilerCuotaService(AlquilerCuotaService alquilerCuotaService) {
		this.alquilerCuotaService = alquilerCuotaService;
	}

	public AlquilerDAO getEntityDAO() {
		return entityDAO;
	}

	@Transactional(readOnly = true)
	public Long getCount(Map<String, String> filters) {
		return getEntityDAO().count(
				getEntityDAO().getSearchPredicates(getEntityDAO().rootCount(),
						filters));
	}

	@Transactional(readOnly = true)
	public List<Alquiler> getList(Integer page, Integer pagesize,
			Map<String, String> filters, String sortField, Boolean asc) {
		return getEntityDAO().listwithPag(
				getEntityDAO().getSearchPredicates(getEntityDAO().rootCount(),
						filters), page, pagesize, sortField, asc);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void save(Alquiler entity) throws BusinessException {
		if (entity.getPeriodos().size() == 0)
			throw new BusinessException(
					"Debe agregar Periodos para completar la operación.");
		if (entity.getPropiedad() == null)
			throw new BusinessException("Debe elegir una propiedad.");
		if (!entity.getPropiedad().getEstado().equals(Estado.D))
			throw new BusinessException(
					"La propiedad esta alquilada o vendida.");

		// Cambiar Estado de Propiedad
		entity.getPropiedad().setEstado(Estado.A);
		// Calcular Fecha Fin Alquiler en funcion de los periodos
		entity.calcularFechaFIn();
		// Generar las Cuotas a partir de los Periodos
		entity.calcularCoutas();
		//Se calcula Deposito Comision Sellado y Ajuste
		Integer asiento = cuotaIngresos(entity);
		
		//Primer Cuota Estado vencida y generar Movimiento de DEBE al inquilino		
		for (AlquilerCuota ca : entity.getCuotas()) {
				if (ca.getNroCuota().equals(1))
				{
					ca.setEstado(CoutaEstado.V);
					getContableService().asientoSimple(entity.getInquilino().getCuenta(), 
													TipoMovimiento.EGRESO,
													MensajesLenguage.getString("cuota.mes.adelantado.vence", ca.getNroCuota().toString(), FuncionesDate.dateToString(ca.getFecha()),entity.getPropiedad().direccionCompleta()), 
													ca.getMontoInquilino().doubleValue(),
													asiento);
				}
		}
			
		getPropiedadService().update(entity.getPropiedad());
		getEntityDAO().create(entity);
	}
	
	

	private Integer cuotaIngresos(Alquiler entity) throws BusinessException {
		CuotaPreIngreso comision = new CuotaPreIngreso(Concepto.C, entity.montoBase().doubleValue(), entity,entity.getFechaInicio(),1);
		CuotaPreIngreso deposito = new CuotaPreIngreso(Concepto.D, entity.montoBase().doubleValue(), entity,entity.getFechaInicio(),2);
		//TODO 10% del todtal del Contrato % 2 (Mitad El Inquilino, Mitad el propietario)   alquiler.montoTotal().doubleValue()/10
		CuotaPreIngreso sellado = new CuotaPreIngreso(Concepto.S, (entity.montoBase().doubleValue()/10D)/2, entity,entity.getFechaInicio(),3);
		
		//TODO  de 1 al 15 Menos /  del 16 al 31   Mas (Porcential a 31 dias)
		Double valor = 0D;
		
		Calendar fechaCuota = Calendar.getInstance();
		fechaCuota.setTime(entity.getFechaInicio());
				
		if((fechaCuota.get(Calendar.DATE)!=1))
		{
			CuotaPreIngreso ajuste;
			if (fechaCuota.get(Calendar.DATE)<=15)
			{
				Double porcentaje  = entity.diasAjuste()*100D/31D;
				Double montoBase = entity.montoBase().doubleValue();
				valor = -(montoBase-porcentaje*montoBase/100);
				valor = Math.round(valor*100.0)/100.0;	
				
				ajuste = new CuotaPreIngreso(Concepto.A, valor, entity,entity.getFechaInicio(),4);
				ajuste.setEstado(CoutaEstado.PT);
				ajuste.setFechaPago(new Date());
			}else 
			{
				Double porcentaje  = entity.diasAjuste()*100D/31D;
				Double montoBase = entity.montoBase().doubleValue();
				valor = montoBase - porcentaje*montoBase/100;
				valor = Math.round(valor*100.0)/100.0;	
				ajuste = new CuotaPreIngreso(Concepto.A, valor, entity,entity.getFechaInicio(),4);
				ajuste.setEstado(CoutaEstado.PE);
			}
			entity.getCuotasPreIngreso().add(ajuste);
		}
		

		entity.getCuotasPreIngreso().add(deposito);
		entity.getCuotasPreIngreso().add(comision);
		entity.getCuotasPreIngreso().add(sellado);
		
		//Agrega los movimientos a la cuenta del Inquilono al Debe (Saldo el Ajuste)
		return getContableService().generarMovimientosPreIngreso(entity);
	}

	@Transactional
	public void delete(Alquiler entity) {
		getEntityDAO().delete(entity);
	}

	@Transactional
	public void update(Alquiler entity) {
		getEntityDAO().update(entity);
	}

	public Alquiler getEntityById(Long id) {
		return getEntityDAO().read(id);
	}

	public List<Alquiler> getAll() {
		return getEntityDAO().findAll();
	}

	public List<Alquiler> findByQuery(String query, String propertyFilter,
			String orderDirection) {
		return getEntityDAO()
				.findByQuery(query, propertyFilter, orderDirection);
	}
	
	
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void alquileresPorVencer() {
		List<Alquiler> alquileresPorVencer = alquileresProximoVencer();
		for (Alquiler alquiler : alquileresPorVencer) {
			alquiler.setEstado(AlquilerEstado.PROXIMO_VENCER);
			update(alquiler);	
			envioMailVenceContrato(alquiler);				
		}
		
	}
	
	public List<Alquiler>  alquileresProximoVencer()
	{
		return getEntityDAO().alquileresPorVencer();
	}
	
	private void envioMailVenceContrato(Alquiler alquiler)
	{		
		
		List<SimpleMailMessage> emails = new ArrayList<SimpleMailMessage>(0);
		
		SimpleMailMessage mailInquilino = new SimpleMailMessage();
		SimpleMailMessage mailPropietario = new SimpleMailMessage();
		
		mailInquilino.setFrom(MensajesAplicacion.getString("application.mail"));
		mailInquilino.setCc(MensajesAplicacion.getString("application.mail"));
		mailInquilino.setTo(alquiler.getInquilino().getMail());
		mailInquilino.setSubject(MensajesLenguage.getString("mail.vence.cotrato.inquilino.subjet"));
		mailInquilino.setText(MensajesLenguage.getString("mail.vence.cotrato.inquilino.msg", alquiler.getPropiedad().direccionCompleta()));
		
		mailPropietario.setFrom(MensajesAplicacion.getString("application.mail"));
		mailPropietario.setTo(MensajesAplicacion.getString("application.mail"));
		mailPropietario.setSubject("Alquiler Proximo a vencer");
		mailPropietario.setText(MensajesLenguage.getString("mail.vence.cotrato.propietario.msg", alquiler.getPropiedad().direccionCompleta(),alquiler.getInquilino().apellidoNombre()));  
				
	
		
		emails.add(mailInquilino);
		emails.add(mailPropietario);
		
		getMailBusness().sendMail(emails.toArray(new SimpleMailMessage[emails.size()]));
	}
	
	
	public List<AlquilerCuota> alquileresActivosCuotaPendientePorMes(int mes)
	{
		return getEntityDAO().alquileresActivosCuotaPendientePorMes(mes);
	}
	
	
}